﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Gko.Utils;
using Gko.Utils.Interfaces;

namespace Gko.Wpf.Mvvm
{
	internal class MessageToActionsMap<TKey>
	{
		private readonly Dictionary<TKey, List<IWeakAction>> map = new Dictionary<TKey, List<IWeakAction>>();

		internal void Add<T>(TKey key, Action<T> callback)
		{
			this.map.GetValueOrAdd(key, new List<IWeakAction>()).Add(new WeakAction<T>(callback));
		}

		internal IEnumerable<Action<T>> GetActions<T>(TKey message)
		{
			List<IWeakAction> actions;
			if (!this.map.TryGetValue(message, out actions)) {
				yield break;
			}

			var weakActions = actions.OfType<WeakAction<T>>().ToArray();
			for (var i = weakActions.Length - 1; i > -1; --i) {
				var weakAction = weakActions[i];
				if (!weakAction.IsAlive) {
					this.map[message].Remove(weakAction);
				} else {
					Action<T> action;
					if (weakAction.TryCreateAction(out action)) {
						yield return action;
					} else {
						this.map[message].Remove(weakAction);
					}
				}
			}

			this.RemoveMessageIfNecessary(this.map[message], message);
		}

		private void RemoveMessageIfNecessary(ICollection<IWeakAction> weakActions, TKey message)
		{
			if (weakActions.Count == 0) {
				this.map.Remove(message);
			}
		}

		internal void RemoveActions(TKey id, object target)
		{
			var actions = this.map[id];
			foreach (var weakAction in actions.ToArray()) {
				if (ReferenceEquals(weakAction.Target, target)) {
					actions.Remove(weakAction);
				}
			}
		}

		internal void RemoveActions(object target)
		{
			foreach (var key in this.map.Keys) {
				this.RemoveActions(key, target);
			}
		}

		//internal IEnumerable<Action<T>> GetActions<T>(TKey key)
		//{
		//    List<IWeakAction> actions;
		//    if (!this.map.TryGetValue(key, out actions)) {
		//        yield break;
		//    }

		//    var actionsToRemove = new List<IWeakAction>();
		//    foreach (var weakAction in actions) {
		//        Action<T> action;
		//        if (weakAction.TryCreateAction(out action)) {
		//            yield return action;
		//        } else {
		//            actionsToRemove.Add(weakAction);
		//        }
		//    }

		//    foreach (var weakAction in actionsToRemove) {
		//        actions.Remove(weakAction);
		//    }
		//}
	}
}
